package ui.component;

import file.FileHandler;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import model.Attribute;
import model.Database;
import model.QueryComponent;
import model.Table;
import model.TableAndAttribute;
import ui.DatabaseHandler;
import ui.converter.TableAndAttributeConverter;

/**
 *
 * @author Merli András-Bertalan
 */
public class QueryView extends VBox {

    private final int TEMPORARY_TABLE_SIZE = 1;
    private final ContentView contentView = new ContentView();
    private boolean groupByAndHaving;
    private Database database;
    private DatabaseHandler databaseHandler;
    private List<String> temporalTableNames;

    public final void updateUi(final Database database, final List<Table> tables, final DatabaseHandler databaseHandler) {
        this.database = database;
        this.databaseHandler = databaseHandler;
        this.temporalTableNames = new LinkedList<>();

        getChildren().clear();

        final ComboBox groupByComboBox = new ComboBox();
        final ComboBox functionAttributeComboBox = new ComboBox();
        final ComboBox functionComboBox = new ComboBox(FXCollections.observableArrayList("COUNT", "SUM", "AVG"));
        final TextField filterTextField = new TextField();
        final ComboBox<String> joinComboBox = new ComboBox(FXCollections.observableArrayList("MERGE", "INL"));
        joinComboBox.setValue("MERGE");
        final TableColumn tableAndAttributeColumn = new TableColumn("Attribute");
        final List<TableAndAttribute> tableAndAttributes = new ArrayList<>();

        // table selection
        final TextArea joinTextArea = new TextArea();
        joinTextArea.setEditable(false);
        joinTextArea.setPromptText("Joins: ");
        final TableView tablesTableView = new TableView(FXCollections.observableArrayList(tables));
        final TableView selectedTablesTableView = new TableView();
        TableColumn nameColumn = new TableColumn("Name");
        nameColumn.setMinWidth(
                100);
        nameColumn.setCellValueFactory(
                new PropertyValueFactory<Table, String>("name"));
        nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        TableColumn nameColumn2 = new TableColumn("Name");
        nameColumn2.setMinWidth(
                100);
        nameColumn2.setCellValueFactory(
                new PropertyValueFactory<Table, String>("name"));
        nameColumn2.setCellFactory(TextFieldTableCell.forTableColumn());
        tablesTableView.getColumns().add(nameColumn);
        selectedTablesTableView.getColumns().add(nameColumn2);

        tablesTableView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent e) {
                Table selectedTable = (Table) tablesTableView.getSelectionModel().getSelectedItem();
                if (e.getClickCount() == 2 && selectedTable != null) {
                    tablesTableView.getItems().remove(selectedTable);
                    selectedTablesTableView.getItems().add(selectedTable);

                    joinTextArea.clear();
                    tableAndAttributes.clear();
                    List<Table> selectedTables = new ArrayList<>(selectedTablesTableView.getItems().size());
                    for (Object o : selectedTablesTableView.getItems()) {
                        selectedTables.add((Table) o);
                    }
                    for (Table t : selectedTables) {
                        for (Attribute a : t.getAttributes()) {
                            tableAndAttributes.add(new TableAndAttribute(t, a));

                            if (a.getReferenceAttribute() != null && a.getReferenceAttributeName() != null) {
                                boolean tableSelected = false;
                                for (Table t1 : selectedTables) {
                                    if (t1.getName().equals(a.getReferenceTableName())) {
                                        tableSelected = true;
                                    }
                                }
                                if (tableSelected) {
                                    joinTextArea.appendText(a.getName() + " = " + a.getReferenceAttributeName().get());
                                }
                            }
                        }
                    }
                    tableAndAttributeColumn.setCellFactory(ComboBoxTableCell.forTableColumn(new TableAndAttributeConverter(), FXCollections.observableArrayList(tableAndAttributes)));
                    groupByComboBox.setItems(FXCollections.observableArrayList(tableAndAttributes));
                    functionAttributeComboBox.setItems(FXCollections.observableArrayList(tableAndAttributes));
                }
            }
        });
        selectedTablesTableView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent e) {
                Table selectedTable = (Table) selectedTablesTableView.getSelectionModel().getSelectedItem();
                if (e.getClickCount() == 2 && selectedTable != null) {
                    selectedTablesTableView.getItems().remove(selectedTable);
                    tablesTableView.getItems().add(selectedTable);

                    tableAndAttributes.clear();
                    for (Object o : selectedTablesTableView.getItems()) {
                        Table t = (Table) o;
                        for (Attribute a : t.getAttributes()) {
                            tableAndAttributes.add(new TableAndAttribute(t, a));
                        }
                    }
                    tableAndAttributeColumn.setCellFactory(ComboBoxTableCell.forTableColumn(new TableAndAttributeConverter(), FXCollections.observableArrayList(tableAndAttributes)));
                }
            }
        });

        //
        final TableView tableView = new TableView();
        final ObservableList<QueryComponent> queryComponents = FXCollections.observableList(new LinkedList<QueryComponent>());
        tableView.setItems(queryComponents);

        tableView.setEditable(
                true);
        final Label label = new Label("Set Query Attributes");

        label.setFont(
                new Font("Arial", 20));

        tableAndAttributeColumn.setMinWidth(150);
        tableAndAttributeColumn.setCellValueFactory(
                new PropertyValueFactory<QueryComponent, Table>("tableAndAttribute"));
        tableAndAttributeColumn.setCellFactory(ComboBoxTableCell.forTableColumn(new TableAndAttributeConverter()));
        tableAndAttributeColumn.setOnEditCommit(
                new EventHandler<TableColumn.CellEditEvent<QueryComponent, TableAndAttribute>>() {
                    @Override
                    public void handle(TableColumn.CellEditEvent<QueryComponent, TableAndAttribute> t) {
                        t.getRowValue().setTableAndAttribute(t.getNewValue());
                    }
                });

        TableColumn filterColumn = new TableColumn("Filter");
        filterColumn.setMinWidth(150);
        filterColumn.setCellValueFactory(
                new PropertyValueFactory<QueryComponent, String>("filter"));
        filterColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        filterColumn.setOnEditCommit(
                new EventHandler<TableColumn.CellEditEvent<QueryComponent, String>>() {
                    @Override
                    public void handle(TableColumn.CellEditEvent<QueryComponent, String> t) {
                        t.getRowValue().setFilter(t.getNewValue());
                    }
                });

        tableView.getColumns().addAll(tableAndAttributeColumn, filterColumn);

        ///////
        final Button addQueryComponentButton = new Button("Add");
        addQueryComponentButton.setOnAction(
                new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent e) {
                        QueryComponent qc = new QueryComponent();
                        qc.setTableAndAttribute(tableAndAttributes.get(0));
                        qc.setFilter("");
                        queryComponents.add(qc);
                    }
                });

        final Button executeQueryButton = new Button("Execute");
        executeQueryButton.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                // selection, projection before join for tables 
                List<Table> resultTables = new LinkedList<>();
                List<QueryComponent> queryComponentsForATable = new LinkedList<>();
                QueryComponent previousQueryComponent = null;
                for (QueryComponent actualQueryComponent : queryComponents) {
                    Table actualQueryComponentTable = actualQueryComponent.getTableAndAttribute().getTable();
                    Table previousQueryComponentTable = null;
                    if (previousQueryComponent != null) {
                        previousQueryComponentTable = previousQueryComponent.getTableAndAttribute().getTable();
                    }
                    if (previousQueryComponent == null || actualQueryComponentTable.equals(previousQueryComponentTable)) {
                        queryComponentsForATable.add(actualQueryComponent);
                    } else {
                        resultTables.add(query(queryComponentsForATable));
                        queryComponentsForATable.clear();
                        queryComponentsForATable.add(actualQueryComponent);
                    }
                    previousQueryComponent = actualQueryComponent;
                }
                if (!queryComponentsForATable.isEmpty()) {
                    resultTables.add(query(queryComponentsForATable));
                    queryComponentsForATable.clear();
                }

                Table jointResultTable = new Table("Query Result", Collections.<Attribute>emptyList());
                if ("MERGE".equals(joinComboBox.getValue())) {
                    // join
                    // merge join
                    if (!resultTables.isEmpty()) {
                        jointResultTable = resultTables.remove(0);
                        while (!resultTables.isEmpty()) {
                            if (FileHandler.loadTemporalTable("temp.txt") != null) {
                                jointResultTable = FileHandler.loadTemporalTable("temp.txt");
                                FileHandler.deleteTemporalTable("temp.txt");
                            }
                            Table resultTableToJoin = null;
                            Attribute jointResultTableAttribute = null;
                            Attribute resultTableToJoinAttribute = null;
                            boolean joinTableFound = false;
                            for (Table resultTable : resultTables) {
                                for (Attribute ajt : jointResultTable.getAttributes()) {
                                    for (Attribute art : resultTable.getAttributes()) {
                                        if (ajt.getType().equals(art.getType())) {
                                            joinTableFound = true;
                                            resultTableToJoin = resultTable;
                                            jointResultTableAttribute = ajt;
                                            resultTableToJoinAttribute = art;
                                            break;
                                        }
                                    }
                                    if (joinTableFound) {
                                        break;
                                    }
                                }
                                if (joinTableFound) {
                                    break;
                                }
                            }
                            jointResultTable = mergeJoin(jointResultTable, resultTableToJoin, jointResultTableAttribute, resultTableToJoinAttribute);
                            jointResultTable.setName(resultTableToJoin.getName());
                            resultTables.remove(resultTableToJoin);
                            FileHandler.saveTemporalTable(jointResultTable, "temp.txt");
                        }
                    }
                } else if ("INL".equals(joinComboBox.getValue())) {
                    // index join
                    if (!resultTables.isEmpty()) {
                        jointResultTable = resultTables.remove(0);
                        while (!resultTables.isEmpty()) {
                            if (FileHandler.loadTemporalTable("temp.txt") != null) {
                                jointResultTable = FileHandler.loadTemporalTable("temp.txt");
                                FileHandler.deleteTemporalTable("temp.txt");
                            }

                            Table resultTableToJoin = null;
                            Attribute jointResultTableAttribute = null;
                            Attribute resultTableToJoinAttribute = null;
                            boolean referenceTableFound = false;
                            for (Attribute a : jointResultTable.getAttributes()) {
                                if (a.getReferenceAttribute() != null && a.getReferenceAttributeName() != null) {
                                    for (Table t : resultTables) {
                                        if (t.getName().equals(a.getReferenceTableName())) {
                                            referenceTableFound = true;
                                            resultTableToJoin = t;
                                            jointResultTableAttribute = a;
                                            for (Attribute a1 : t.getAttributes()) {
                                                if (a.getType().equals(a1.getType())) {
                                                    resultTableToJoinAttribute = a1;
                                                    break;
                                                }
                                            }
                                        }
                                        if (referenceTableFound) {
                                            break;
                                        }
                                    }
                                }
                                if (referenceTableFound) {
                                    break;
                                }
                            }
                            jointResultTable = indexJoin(jointResultTable, resultTableToJoin, jointResultTableAttribute, resultTableToJoinAttribute);
                            System.out.println("jrt: " + jointResultTable + "rtj: " + resultTableToJoin);
                            System.out.println("jrtn: " + jointResultTable.getName());
                            System.out.println("rtjn: " + resultTableToJoin.getName());
                            jointResultTable.setName(resultTableToJoin.getName());
                            resultTables.remove(resultTableToJoin);
                            FileHandler.saveTemporalTable(jointResultTable, "temp.txt");
                        }
                    }
                }
                FileHandler.deleteTemporalTable("temp.txt");

                // group by, having
                // remove?
                LinkedList<QueryComponent> queryComponentsClone = new LinkedList<>(queryComponents);
                if (groupByAndHaving) {
                    TableAndAttribute groupByAttribute = (TableAndAttribute) groupByComboBox.getValue();
                    int groupByAttributeIndex = 0;
                    for (int i = 0; i < queryComponentsClone.size(); i++) {
                        if (queryComponentsClone.get(i).getTableAndAttribute().equals(groupByAttribute)) {
                            groupByAttributeIndex = i;
                        }
                    }

                    List<List<String>> temporalTableValues = new LinkedList<>();
                    int index = 0;
                    for (index = 0; index < jointResultTable.getValues().size(); index++) {
                        temporalTableValues.add(jointResultTable.getValues().get(index));
                        if (index != 0 && index % TEMPORARY_TABLE_SIZE == 0) {
                            try {
                                groupBy(temporalTableValues, groupByAttributeIndex);
                                temporalTableNames.add("temp_" + index);
                                mongodb.MongoDb.saveTemporalTable(database, temporalTableValues, "temp_" + index);
                                temporalTableValues.clear();
                            } catch (UnknownHostException ex) {
                                Logger.getLogger(QueryView.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }
                    if (!temporalTableValues.isEmpty()) {
                        try {
                            groupBy(temporalTableValues, groupByAttributeIndex);
                            temporalTableNames.add("temp_" + index);
                            mongodb.MongoDb.saveTemporalTable(database, temporalTableValues, "temp_" + index);
                            temporalTableValues.clear();
                        } catch (UnknownHostException ex) {
                            Logger.getLogger(QueryView.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
//                    groupBy(jointResultTable.getValues(), groupByAttributeIndex);

                    TableAndAttribute functionAttribute = (TableAndAttribute) functionAttributeComboBox.getValue();
                    if (functionAttribute != null) {
                        int functionAttributeIndex = 0;
                        for (int i = 0; i < queryComponentsClone.size(); i++) {
                            if (queryComponentsClone.get(i).getTableAndAttribute().equals(functionAttribute)) {
                                functionAttributeIndex = i;
                            }
                        }
                        jointResultTable.setValues(aggregate(jointResultTable.getValues(), groupByAttributeIndex, functionAttributeIndex, (String) functionComboBox.getValue(), filterTextField.getText()));
                        jointResultTable.getAttributes().get(functionAttributeIndex).setName(functionComboBox.getValue() + " " + jointResultTable.getAttributes().get(functionAttributeIndex).getName());
                    }
                }
                contentView.updateUi(
                        null, jointResultTable, null, databaseHandler);
                contentView.hideAddAndDeleteButton();
            }
        }
        );

        // group by, having
        groupByComboBox.setPromptText(
                "Group By: ");
        groupByComboBox.setConverter(
                new TableAndAttributeConverter());
        functionComboBox.setPromptText(
                "Function: ");
        functionAttributeComboBox.setPromptText(
                "Attribute: ");
        functionAttributeComboBox.setConverter(
                new TableAndAttributeConverter());
        filterTextField.setPromptText(
                "Filter: ");
        final Button saveGroupByHavingButton = new Button("Save");
        final HBox groupByHavingBox = new HBox(groupByComboBox, functionComboBox, functionAttributeComboBox, filterTextField, saveGroupByHavingButton);

        saveGroupByHavingButton.setOnAction(
                new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent event
                    ) {
                        groupByAndHaving = true;
                        groupByHavingBox.setDisable(true);
                    }
                }
        );

        //
        HBox tableSelectionBox = new HBox(tablesTableView, selectedTablesTableView);
        HBox joinBox = new HBox(joinTextArea, joinComboBox);

        setSpacing(
                5);
        setPadding(
                new Insets(10, 0, 0, 10));
        getChildren()
                .remove(contentView);
        getChildren()
                .addAll(label, tableSelectionBox, joinBox, tableView, addQueryComponentButton, groupByHavingBox, executeQueryButton, contentView);
    }

    private Table mergeJoin(Table table1, Table table2, Attribute attribute1, Attribute attribute2) {
        List<Attribute> resultTableAttributes = new LinkedList<>(table1.getAttributes());
        resultTableAttributes.addAll(table2.getAttributes());
        Table resultTable = new Table("Merge Join Result", resultTableAttributes);
        resultTable.setValues(new LinkedList<List<String>>());

        int table1Index = 0;
        int table2Index = 0;
        int table1AttributeIndex = table1.getAttributes().indexOf(attribute1);
        int table2AttributeIndex = table2.getAttributes().indexOf(attribute2);
        List<List<String>> table1Values = table1.getValues();
        List<List<String>> table2Values = table2.getValues();
        groupBy(table1.getValues(), table1AttributeIndex);
        groupBy(table2.getValues(), table2AttributeIndex);

        while (table1Index < table1Values.size() && table2Index < table2Values.size()) {
            if (table1Values.get(table1Index).get(table1AttributeIndex).equals(table2Values.get(table2Index).get(table2AttributeIndex))) {
                List<String> resultRow = new LinkedList<>(table1Values.get(table1Index));
                resultRow.addAll(table2Values.get(table2Index));
                resultTable.getValues().add(resultRow);
                if (table1Index + 1 < table1Values.size() && table2Index + 1 < table2Values.size()) {
                    if (table1Values.get(table1Index + 1).get(table1AttributeIndex).compareTo(table2Values.get(table2Index + 1).get(table2AttributeIndex)) < 0) {
                        table1Index++;
                    } else {
                        table2Index++;
                    }
                } else if (table1Index + 1 < table1Values.size()) {
                    table1Index++;
                } else {
                    table2Index++;
                }
            } else {
                if (table1Values.get(table1Index).get(table1AttributeIndex).compareTo(table2Values.get(table2Index).get(table2AttributeIndex)) < 0) {
                    table1Index++;
                } else {
                    table2Index++;
                }
            }
        }
        return resultTable;
    }

    private Table indexJoin(Table table1, Table table2, Attribute attribute1, Attribute attribute2) {
        Table indexTable = null;
        Table indexedTable = null;
        Table notIndexedTable = null;
        Attribute indexedTablePrimaryAttribute = null;
        Attribute notIndexedTablePrimaryAttribute = null;
        if (attribute2 != null && attribute2.getIsIndexed()) {
            indexTable = databaseHandler.getIndexTableByDatabaseAndTableName(database, table2.getName() + "_" + attribute2.getName());
            indexedTable = table2;
            notIndexedTable = table1;
            for (Attribute a : indexedTable.getAttributes()) {
                if (a.getIsPrimary()) {
                    indexedTablePrimaryAttribute = a;
                }
            }
            for (Attribute a : notIndexedTable.getAttributes()) {
                if (a.getIsPrimary()) {
                    notIndexedTablePrimaryAttribute = a;
                }
            }
        } else if (attribute1 != null && attribute1.getIsIndexed()) {
            indexTable = databaseHandler.getIndexTableByDatabaseAndTableName(database, table1.getName() + "_" + attribute1.getName());
            indexedTable = table1;
            notIndexedTable = table2;
            for (Attribute a : indexedTable.getAttributes()) {
                if (a.getIsPrimary()) {
                    indexedTablePrimaryAttribute = a;
                }
            }
            for (Attribute a : notIndexedTable.getAttributes()) {
                if (a.getIsPrimary()) {
                    notIndexedTablePrimaryAttribute = a;
                }
            }
        }

        Table resultTable = null;

        if (indexTable != null) {
            List<Attribute> resultTableAttributes = new LinkedList<>(indexedTable.getAttributes());
            resultTableAttributes.addAll(notIndexedTable.getAttributes());
            resultTable = new Table("IndexJoinResult", resultTableAttributes);
            resultTable.setValues(new LinkedList<List<String>>());

            int indexedTablePrimaryAttributeIndex = indexedTable.getAttributes().indexOf(indexedTablePrimaryAttribute);
            int notIndexedTablePrimaryAttributeIndex = notIndexedTable.getAttributes().indexOf(notIndexedTablePrimaryAttribute);
            List<List<String>> indexedTableValues = indexedTable.getValues();
            List<List<String>> notIndexedTableValues = notIndexedTable.getValues();

            for (List<String> notIndexedTableRow : notIndexedTableValues) {
                try {
                    String notIndexedPrimaryAttributeValue = notIndexedTableRow.get(notIndexedTablePrimaryAttributeIndex);
                    List<String> indexValues = mongodb.MongoDb.getIndexValuesByKeyFilter(database, indexTable, "= " + notIndexedPrimaryAttributeValue);
                    for (String indexValue : indexValues) {
                        for (List<String> indexedTableRow : indexedTableValues) {
                            List<String> resultTableRow = new LinkedList<>(indexedTableRow);
                            if (indexedTableRow.get(indexedTablePrimaryAttributeIndex).equals(indexValue)) {
                                resultTableRow.addAll(notIndexedTableRow);
                                resultTable.getValues().add(resultTableRow);
                            }
                        }
                    }
                } catch (UnknownHostException ex) {
                    Logger.getLogger(QueryView.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

//            for (List<String> indexedTableRow : indexedTableValues) {
//                try {
//                    String indexedAttributeValue = indexedTableRow.get(indexedAttributeIndex);
//                    List<String> indexValues = mongodb.MongoDb.getIndexValuesByKeyFilter(database, indexTable, "= " + indexedAttributeValue);
//                    for (String indexValue : indexValues) {
//                        for (List<String> notIndexedTableRow : notIndexedTableValues) {
//                            List<String> resultTableRow = new LinkedList<>(indexedTableRow);
//                            System.out.println("!!! " + notIndexedTableValues.get(notIndexedAttributeIndex) + " " + indexValue);
//                            if (notIndexedTableRow.get(notIndexedAttributeIndex).equals(indexValue)) {
//                                resultTableRow.addAll(notIndexedTableRow);
//                                resultTable.getValues().add(resultTableRow);
//                            }
//                        }
//                    }
//                } catch (UnknownHostException ex) {
//                    Logger.getLogger(QueryView.class.getName()).log(Level.SEVERE, null, ex);
//                }
//            }
        }
        return resultTable;
    }

    private Table query(List<QueryComponent> queryComponents) {
        Table resultTable = new Table("Query Result", new LinkedList<Attribute>());
        resultTable.setValues(new LinkedList<List<String>>());

        if (!queryComponents.isEmpty()) {
            Table table = queryComponents.get(0).getTableAndAttribute().getTable();
            resultTable.setName(table.getName());
            List<List<String>> values = table.getValues();
            List<List<String>> resultValues = resultTable.getValues();
            for (List<String> value : values) {
                List<String> row = new LinkedList<>();
                row.add("true");
                resultValues.add(row);
            }
        }

        // parse first the attributes with indexes
        try {
            Iterator<QueryComponent> iterator = queryComponents.iterator();
            while (iterator.hasNext()) {
                QueryComponent queryComponent = iterator.next();
                Attribute attribute = queryComponent.getTableAndAttribute().getAttribute();
                boolean firstlyParsedAttribute = false;

                if (attribute.getIsIndexed()) {
                    Table table = queryComponent.getTableAndAttribute().getTable();
                    String filter = queryComponent.getFilter();
                    int columnIndex = table.getAttributes().indexOf(attribute);
                    int primaryAttributeIndex = 0;
                    for (int i = 0; i < table.getAttributes().size(); i++) {
                        if (table.getAttributes().get(i).getIsPrimary()) {
                            primaryAttributeIndex = i;
                        }
                    }
                    String indexTableName = table.getName() + "_" + attribute.getName();
                    Table indexTable = databaseHandler.getIndexTableByDatabaseAndTableName(database, indexTableName);

                    List<List<String>> values = table.getValues();
                    List<List<String>> resultValues = resultTable.getValues();
                    List<String> primaryKeys;
                    primaryKeys = mongodb.MongoDb.getIndexValuesByKeyFilter(database, indexTable, filter);

                    for (int i = 0; i < values.size(); i++) {
                        List<String> row = values.get(i);
                        List<String> resultRow = resultValues.get(i);
                        if (Boolean.valueOf(resultRow.get(0))) {
                            if (primaryKeys.contains(row.get(primaryAttributeIndex))) {
                                String possibleValue = row.get(columnIndex);
                                if (!resultTable.getAttributes().contains(attribute)) {
                                    resultTable.getAttributes().add(attribute);
                                    firstlyParsedAttribute = true;
                                }
                                if (firstlyParsedAttribute) {
                                    resultRow.add(possibleValue);
                                }
                            } else {
                                resultRow.set(0, "false");
                            }
                        }
                    }
                    iterator.remove();
                }
            }
        } catch (UnknownHostException ex) {
            Logger.getLogger(QueryView.class.getName()).log(Level.SEVERE, null, ex);
        }

        // parse the attributes without indexes
        for (QueryComponent queryComponent : queryComponents) {
            Table table = queryComponent.getTableAndAttribute().getTable();
            Attribute attribute = queryComponent.getTableAndAttribute().getAttribute();
            boolean firstlyParsedAttribute = false;
            String filter = queryComponent.getFilter();

            List<List<String>> values = table.getValues();
            List<List<String>> resultValues = resultTable.getValues();

            int columnIndex = table.getAttributes().indexOf(attribute);

            for (int i = 0; i < values.size(); i++) {
                List<String> row = values.get(i);
                List<String> resultRow = resultValues.get(i);
                if (Boolean.valueOf(resultRow.get(0))) {
                    boolean ok = true;
                    String possibleValue = row.get(columnIndex);

                    if (!filter.equals("")) {
                        String comparable = filter.substring(2);

                        if (filter.startsWith("= ")) {
                            ok = possibleValue.equals(comparable);
                        } else if (filter.startsWith("< ")) {
                            ok = Integer.valueOf(possibleValue) < Integer.valueOf(comparable);
                        } else if (filter.startsWith("> ")) {
                            ok = Integer.valueOf(possibleValue) > Integer.valueOf(comparable);
                        } else if (filter.startsWith("L ")) {
                            ok = possibleValue.matches(comparable);
                        }
                    }

                    if (ok) {
                        if (!resultTable.getAttributes().contains(attribute)) {
                            resultTable.getAttributes().add(attribute);
                            firstlyParsedAttribute = true;
                        }
                        if (firstlyParsedAttribute) {
                            resultRow.add(possibleValue);
                        }
                    } else {
                        resultRow.set(0, "false");
                    }
                }
            }
        }

        Iterator<List<String>> iterator = resultTable.getValues().iterator();
        while (iterator.hasNext()) {
            List<String> value = iterator.next();
//                    for (String s : value) {
//                        System.out.print(s + " ");
//                    }
//                    System.out.println("");
            boolean rowIsOk = Boolean.valueOf(value.get(0));
            if (!rowIsOk) {
                iterator.remove();
            } else {
                value.remove(0);
            }
        }
        return resultTable;
    }

    private void groupBy(List<List<String>> values, int columnIndex) {
        for (int i = 0; i < values.size(); i++) {
            for (int j = i + 1; j < values.size(); j++) {
                if (values.get(i).get(columnIndex).compareTo(values.get(j).get(columnIndex)) > 0) {
                    Collections.swap(values, i, j);
                }
            }
        }
    }

    private List<List<String>> aggregate(List<List<String>> values, int groupByIndex, int functionAttributeIndex, String functionName, String filter) {
        List<List<String>> newValues = new ArrayList<>();
        String previousGroupByValue = null;
        List<String> previousRow = null;
        int aggregateValue = 0;
        int count = 0;
//        for (List<String> row : values) {
        while (!temporalTableNames.isEmpty()) {
            try {
                List<String> row = mongodb.MongoDb.getMinFromTemporalTables(database, temporalTableNames, groupByIndex);
                if (row.get(groupByIndex).equals(previousGroupByValue) || previousGroupByValue == null) {
                    if (null != functionName) {
                        switch (functionName) {
                            case "SUM":
                                aggregateValue += Integer.parseInt(row.get(functionAttributeIndex));
                                break;
                            case "COUNT":
                                aggregateValue += 1;
                                break;
                            case "AVG":
                                aggregateValue += Integer.parseInt(row.get(functionAttributeIndex));
                                break;
                        }
                        count += 1;
                    }
                } else {
                    if (previousRow != null) {
                        if (null != functionName) {
                            switch (functionName) {
                                case "AVG":
                                    previousRow.set(functionAttributeIndex, String.valueOf((double) aggregateValue / count));
                                    break;
                                case "SUM":
                                case "COUNT":
                                    previousRow.set(functionAttributeIndex, String.valueOf(aggregateValue));
                                    break;
                            }
                        }
                    }
                    boolean ok = true;
                    if (!filter.equals("") && previousRow != null) {
                        String comparable = filter.substring(2);
                        String possibleValue = previousRow.get(functionAttributeIndex);

                        if (filter.startsWith("= ")) {
                            ok = possibleValue.equals(comparable);
                        } else if (filter.startsWith("< ")) {
                            ok = Integer.valueOf(possibleValue) < Integer.valueOf(comparable);
                        } else if (filter.startsWith("> ")) {
                            ok = Integer.valueOf(possibleValue) > Integer.valueOf(comparable);
                        } else if (filter.startsWith("L ")) {
                            ok = possibleValue.matches(comparable);
                        }
                    }
                    if (ok) {
                        newValues.add(previousRow);
                    }
                    if (null != functionName) {
                        switch (functionName) {
                            case "SUM":
                            case "AVG":
                                aggregateValue = Integer.parseInt(row.get(functionAttributeIndex));
                                break;
                            case "COUNT":
                                aggregateValue = 1;
                                break;
                        }
                        count = 1;
                    }
                }
                previousGroupByValue = row.get(groupByIndex);
                previousRow = row;
            } catch (UnknownHostException ex) {
                Logger.getLogger(QueryView.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        if (aggregateValue > 0 && previousRow != null) {
            previousRow.set(functionAttributeIndex, String.valueOf(aggregateValue));
        }
        boolean ok = true;
        if (!filter.equals("") && previousRow != null) {
            String comparable = filter.substring(2);
            String possibleValue = previousRow.get(functionAttributeIndex);

            if (filter.startsWith("= ")) {
                ok = possibleValue.equals(comparable);
            } else if (filter.startsWith("< ")) {
                ok = Integer.valueOf(possibleValue) < Integer.valueOf(comparable);
            } else if (filter.startsWith("> ")) {
                ok = Integer.valueOf(possibleValue) > Integer.valueOf(comparable);
            } else if (filter.startsWith("L ")) {
                ok = possibleValue.matches(comparable);
            }
        }
        if (ok) {
            newValues.add(previousRow);
        }

        return newValues;
    }
}
